' Written by Thomas Berezansky, Merrimack Valley Library Consortium
' Based in part on Code by DB, Memorial Hall Library and CK, Chelmsford Public Library
' Their code was based on code by Michael Wofsey of the Public Library of Brookline
' RawPrinterHelper code originally from Microsoft. See RawPrinterHelper.vb for more information.

'Copyright (c) 2009, Merrimack Valley Library Consortium
'Copyright (c) 2012, Jay Miley
'All rights reserved.
'
'Redistribution and use in source and binary forms, with or without modification, are permitted
'provided that the following conditions are met:
'
'    * Redistributions of source code must retain the above copyright notice, this list of
'      conditions and the following disclaimer.
'    * Redistributions in binary form must reproduce the above copyright notice, this list of
'      conditions and the following disclaimer in the documentation and/or other materials
'      provided with the distribution.
'    * Neither the name of the Merrimack Valley Library Consortium nor the names of its
'      contributors may be used to endorse or promote products derived from this software without
'      specific prior written permission.
'
'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
'IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
'AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
'CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
'CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
'SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
'THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
'OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
'POSSIBILITY OF SUCH DAMAGE.

Imports System.IO ' File Operations
Imports System.IO.Pipes ' Pipe Stuff
Imports System.Xml ' XML reading for config files
Imports System.Text.RegularExpressions ' For Regular Expressions
Imports Microsoft.Win32 ' Registry
Imports System.Windows.Forms ' Notify Icon

Module SymphonyReceiptPrinter
#Region "EpsonCodes"
    Friend Const epsonLineFeedAmount10 As String = Chr(&H1B) & "3" & Chr(10) ' Approx 10/6 inch?
    Friend Const epsonLineFeedAmount20 As String = Chr(&H1B) & "3" & Chr(20) ' Approx 20/6 inch?
    Friend Const epsonLineFeedAmount1 As String = Chr(&H1B) & "3" & Chr(1) ' Approx 1/6 inch?
    Friend Const epsonLineFeed As String = Chr(&H1B) & "d" ' Need a Chr(#) for # of lines to linefeed
    Friend Const epsonSizeNormal As String = Chr(&H1D) & "!" & Chr(0) ' Vertical: 1, Horizontal: 1
    Friend Const epsonSizeDouble As String = Chr(&H1D) & "!" & Chr(17) ' Vertical: 2, Horizontal: 2
    Friend Const epsonSizeTriple As String = Chr(&H1D) & "!" & Chr(34) ' Vertical: 3, Horizontal: 3
    Friend Const epsonSizeTripleWide As String = Chr(&H1D) & "!" & Chr(50) ' Vertical: 3, Horizontal: 4
    Friend Const epsonSizeTripleDoubleWide As String = Chr(&H1D) & "!" & Chr(66) ' Vertical: 3, Horizontal: 5
    Friend Const epsonSizeQuadruple As String = Chr(&H1D) & "!" & Chr(51) ' Vertical: 4, Horizontal: 4
    Friend Const epsonSizeQuintuple As String = Chr(&H1D) & "!" & Chr(68) ' Vertical: 5, Horizontal: 5
    Friend Const epsonSizeSextuple As String = Chr(&H1D) & "!" & Chr(85) ' Vertical: 6, Horizontal: 6
    Friend Const epsonResetFormat As String = Chr(&H1B) & "!" & Chr(0) ' & Chr(0) ' Second null not needed?
    Friend Const epsonCutPaper As String = Chr(&H1D) & "V" & Chr(1) 'Chr(&H1B) & Chr(&H69)
    Friend Const epsonSmoothingOn As String = Chr(&H1D) & "b" & Chr(1) ' Was Chr(127), but only the lowest bit counts
    Friend Const epsonSmoothingOff As String = Chr(&H1D) & "b" & Chr(0)
    Friend Const epsonEmphasisOn As String = Chr(&H1B) & "E" & Chr(1)
    Friend Const epsonEmphasisOff As String = Chr(&H1B) & "E" & Chr(0)
    Friend Const epsonAlignLeft As String = Chr(&H1B) & "a" & Chr(0)
    Friend Const epsonAlignCenter As String = Chr(&H1B) & "a" & Chr(1)
    Friend Const epsonAlignRight As String = Chr(&H1B) & "a" & Chr(2)
    Friend Const epsonInvertOn As String = Chr(&H1B) & "{" & Chr(1)
    Friend Const epsonInvertOff As String = Chr(&H1B) & "{" & Chr(0)
    Friend Const epsonInitialize As String = Chr(&H1B) & "@" ' Resets state to initial power-on state for all settings
    Friend Const epsonVerticalPrintOn As String = Chr(&H1B) & "T" & Chr(3)
    Friend Const epsonVerticalPrintOff As String = Chr(&H1B) & "T" & Chr(48)
    Friend Const epsonLargerPrint As String = Chr(&H1D) & "!" & Chr(1)
    Friend Const epsonRotateOn As String = Chr(&H1B) & "V" & Chr(1)
    Friend Const epsonRotateOff As String = Chr(&H1B) & "V" & Chr(0)
    Friend Const epsonModeStandard As String = Chr(&H1D) & "S"
    Friend Const epsonModePage As String = Chr(&H1B) & "L"
    Friend Const epsonPageSize As String = Chr(&H1B) & "W" & Chr(0) & Chr(0) & Chr(0) & Chr(0) & Chr(127) & Chr(0) & Chr(127) & Chr(1)
    Friend Const epsonPrintPageMode As String = Chr(&H1D) & "F"

#End Region
#Region "Local Vars"
#Region "Enums"
    Private Enum HoldDate
        None = 0
        Left = 1
        Right = 2
        Center = 3
    End Enum
    Private Enum LastNameSize
        Normal = 1
        Doubl = 2
        Tripl = 3
    End Enum
    Private Enum FirstNameSize
        Normal = 1
        Doubl = 2
        Tripl = 3
    End Enum
    Private Enum HoldNamePosition
        Top = 0
        TopFlipped = 1
        Flipped = 1 ' This is to make bitwise "flipped" more readable
        Bottom = 2
        Off = 4
    End Enum
#End Region
    ' Overall settings - Loaded from config file
    Private myPrinter As String = ""
    Private myTopMessageFile As String = ""
    Private myBottomMessageFile As String = ""
    Private myAgencyFile As String = "agencies.xml"
    Private myPhoneNumber As String = ""
    Private myWhiteSpaceTop As Integer = 0
    Private myWhiteSpaceBottom As Integer = 1
    Private myAutoCut As Boolean = True
    Private myConfigHorizon As Boolean = True

    ' Hold Slip settings - Loaded from config file
    Private myHoldDate As HoldDate = HoldDate.Right
    Private myHoldDateAdd As Integer = 7
    Private myHoldDateYear As Boolean = False
    Private myHoldCircDate As Boolean = True
    Private myHoldPatronBarcode As Boolean = False
    Private myHoldPatronBarcodeMask As Integer = 0
    Private myHoldEMail As Boolean = False
    Private myHoldPhone As Boolean = False
    Private myHoldWhiteSpaceBottom As Integer = 0
    Private myHoldName As Integer = 1
    Private myHoldNamePosition As HoldNamePosition = HoldNamePosition.Top
    Private myHoldLastNameSize As LastNameSize = LastNameSize.Tripl
    Private myHoldFirstNameSize As FirstNameSize = FirstNameSize.Doubl
    Private myHoldPhoneLarger As Boolean = False
    Private myHoldCallSlips As Integer = 0

    'Hold Slip Wrapper settings - Loaded from config file
    Private myHoldWrapper As Boolean = False
    Private myHoldWrapperUserLastNameLength As Integer = 4
    Private myHoldWrapperUserFirstNameLength As Integer = 3
    Private myHoldWrapperUserIDLength As Integer = 4
    Private myHoldWrapperLastNameSize As Integer = 0
    Private myHoldWrapperFirstNameSize As Integer = 0
    Private myHoldWrapperUserIDSize As Integer = 0
    Private myHoldWrapperHoldUntilSize As Integer = 0

    ' Checkout Slip settings - Loaded from config file
    Private myCheckoutPhone As Boolean = False
    Private myCheckoutPatronBarcodeMask As Integer = 0
    Private myCheckoutDueDates As Integer = 0 'originally boolean

    ' Checkin Slip settings - Loaded from config file
    Private myCheckinPhone As Boolean = False
    Private myCheckinPatronBarcodeMask As Integer = 0
    Private myCheckinOff As Boolean = False
    ' Private myCheckinTempForce As MenuItem = Nothing

    ' Transit Slip settings - Loaded from config file
    Private myTransitWhiteSpaceBottom As Integer = 0
    Private myTransitMiniWhiteSpaceBottom As Integer = 0

    'Payment Slip settings - Loaded from config file
    Private myPaymentSlips As Integer = 0
    Private myPaymentPatronBarcodeMask As Integer = 0

    ' Agency Information File
    Private myAgencyDocument As XmlDocument = New XmlDocument()
    Private myAgenciesDisabled As Boolean = False

    ' Pipe Stuff
    Private pipeServer(0 To 1) As NamedPipeServerStream ' Would say just "1" there, but 0 to 1 makes it more obvious what is going on
    Private pipeAsyncCallBack As New AsyncCallback(AddressOf pipeCallBack)
    Private pipeOpen(pipeServer.GetUpperBound(0)) As Boolean
    Private myPipeResult(pipeServer.GetUpperBound(0)) As IAsyncResult

    ' Tray Icon
    Private myNotifyIcon As NotifyIcon
    Private myContextMenu As ContextMenu

    ' Cross-Function error strings
    Private myErrorString As String = ""
#End Region

    Private Sub SettingsReset()
        ' Overall settings
        myPrinter = ""
        myTopMessageFile = ""
        myBottomMessageFile = ""
        myAgencyFile = "agencies.xml"
        myPhoneNumber = ""
        myWhiteSpaceTop = 0
        myWhiteSpaceBottom = 1
        myAutoCut = True
        myConfigHorizon = True

        ' Hold Slip settings
        myHoldDate = HoldDate.Right
        myHoldDateAdd = 7
        myHoldDateYear = False
        myHoldCircDate = True
        myHoldPatronBarcode = False
        myHoldPatronBarcodeMask = 0
        myHoldEMail = False
        myHoldPhone = False
        myHoldWhiteSpaceBottom = 0
        myHoldName = 1
        myHoldNamePosition = HoldNamePosition.Top
        myHoldPhoneLarger = False
        myHoldCallSlips = 0
        myHoldLastNameSize = LastNameSize.Tripl
        myHoldFirstNameSize = 2

        ' Checkout Slip settings
        myCheckoutPhone = False
        myCheckoutPatronBarcodeMask = 0
        myCheckoutDueDates = 0

        ' Checkin Slip settings
        myCheckinPhone = False
        myCheckinPatronBarcodeMask = 0
        myCheckinOff = False

        ' Transit Slip settings
        myTransitWhiteSpaceBottom = 0
        myTransitMiniWhiteSpaceBottom = 0

        ' Hold Wrapper Settings

    End Sub

    Sub Main()
        ' myCheckinTempForce = New MenuItem("Allow Next CheckIn Slip", AddressOf HandleCheckIn)
        myContextMenu = New ContextMenu(New MenuItem() { _
                New MenuItem("Reload Configuration", AddressOf HandleReload), _
                New MenuItem("Reset Printer", AddressOf HandleReset), _
                New MenuItem("-"), _
                New MenuItem("Close Symphony Receipt Printer", AddressOf HandleExit)})
        myNotifyIcon = New NotifyIcon()
        myNotifyIcon.Icon = New System.Drawing.Icon(System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream("SymphonyReceiptPrinter.Crystal_printer1.ico"))
        myNotifyIcon.Text = "Symphony Receipt Printer - " & My.Application.Info.Version.ToString()
        myNotifyIcon.ContextMenu = myContextMenu
        myNotifyIcon.Visible = True
        Console.WriteLine("Symphony Receipt Printer - Version " & My.Application.Info.Version.ToString())
        Console.WriteLine("Debug Information:")

        If Not RawPrinterHelper.ValidPrinter("Symphony Receipt Printer") Then
            myErrorString &= "Error: ""Symphony Receipt Printer"" printer not found." & vbCrLf & "This program may not function properly without it." & vbCrLf
        End If
        LoadConfigFile()
        LoadAgencies()
        'If myConfigHorizon Then
        '    Console.WriteLine("Configuring Symphoony")
        '    configHorizon()
        'End If
        If myErrorString.Length > 0 Then
            myNotifyIcon.ShowBalloonTip(30, "Symphony Receipt Printer", myErrorString.Trim, ToolTipIcon.Warning)
            myErrorString = String.Empty
        End If
        ' Note: We open multiple so that the pipe doesn't block between jobs
        ' In theory, we will only ever need 2, but the loop ensures that number can be increased above as needed
        Console.WriteLine("Opening Pipes")
        Dim i As Integer
        For i = 0 To pipeServer.GetUpperBound(0)
            pipeServer(i) = New NamedPipeServerStream("HZReceipt", PipeDirection.In, 10, PipeTransmissionMode.Message, PipeOptions.Asynchronous)
            myPipeResult(i) = pipeServer(i).BeginWaitForConnection(pipeAsyncCallBack, pipeOpen(i))
            pipeOpen(i) = True
        Next
        Console.WriteLine("Beginning Run Loop")
        Application.Run()
        Console.WriteLine("Run Loop Complete")
        myNotifyIcon.Visible = False
        myNotifyIcon.Dispose()
        Console.WriteLine("Closing Pipes")
        For i = 0 To pipeServer.GetUpperBound(0)
            pipeOpen(i) = False
            pipeServer(i).Close()
        Next
        Console.WriteLine("Done")
    End Sub

    Sub HandleExit(ByVal sender As Object, ByVal e As EventArgs)
        Application.Exit()
    End Sub

    Sub HandleReload(ByVal sender As Object, ByVal e As EventArgs)
        myErrorString = String.Empty
        LoadConfigFile()
        LoadAgencies()
        If myErrorString.Length > 0 Then
            myNotifyIcon.ShowBalloonTip(30, "Symphony Receipt Printer", myErrorString.Trim, ToolTipIcon.Warning)
            myErrorString = String.Empty
        Else
            myNotifyIcon.ShowBalloonTip(30, "Symphony Receipt Printer", "Configuration Reloaded", ToolTipIcon.Info)
        End If
    End Sub

    Sub HandleReset(ByVal sender As Object, ByVal e As EventArgs)
        RawPrinterHelper.SendStringToPrinter(myPrinter, epsonInitialize)
    End Sub

    'Sub HandleCheckIn(ByVal sender As Object, ByVal e As EventArgs)
    '    myCheckinTempForce.Checked = Not myCheckinTempForce.Checked
    'End Sub

    Private Sub LoadConfigFile()
        Console.WriteLine("Start Config Load")
        Dim configFile As XmlDocument = New XmlDocument()
        Dim configFilename As String = ""
        Dim foundDocument As Boolean = False
        Dim allowUpdate As Boolean = True

        SettingsReset() ' Reset settings

        If Environment.GetCommandLineArgs.Length > 1 Then
            Try
                configFile.Load(Environment.GetCommandLineArgs(1))
                configFilename = Environment.GetCommandLineArgs(1)
                foundDocument = True
            Catch ex As Exception
                myErrorString &= "Failed to load " & Environment.GetCommandLineArgs(1) & vbCrLf
                Console.WriteLine("Failed to load " & Environment.GetCommandLineArgs(1))
                Console.WriteLine("Error: " & ex.Message)
                allowUpdate = False
            End Try
        End If
        If Not foundDocument Then
            Try
                configFile.Load("settings.xml")
                configFilename = "settings.xml"
                foundDocument = True
            Catch ex As Exception
                myErrorString &= "Failed to load settings.xml" & vbCrLf
                Console.WriteLine("Failed to load settings.xml")
                Console.WriteLine("Error: " & ex.Message)
                allowUpdate = False
            End Try
        End If
        If Not foundDocument And Not Environment.CurrentDirectory.Equals(My.Application.Info.DirectoryPath) Then
            Try
                configFile.Load(My.Application.Info.DirectoryPath & "\settings.xml")
                configFilename = My.Application.Info.DirectoryPath & "\settings.xml"
                foundDocument = True
            Catch ex As Exception
                myErrorString &= "Failed to load " & My.Application.Info.DirectoryPath & "\settings.xml" & vbCrLf
                Console.WriteLine("Failed to load " & My.Application.Info.DirectoryPath & "\settings.xml")
                Console.WriteLine("Error: " & ex.Message)
                allowUpdate = False ' Should NEVER get to this point without this being set, but just in case
            End Try
        End If

        If Not foundDocument Then
            FatalError("Failed to load configuration file(s).")
        Else
            Dim configNode As XmlNode
            configNode = configFile.SelectSingleNode("HZReceipt/Printer")
            If configNode Is Nothing Then
                Console.WriteLine("FATAL ERROR: Printer not defined in config!")
                FatalError("Need a printer in the configuration file to continue")
            Else
                myPrinter = configNode.InnerText.Trim
                If myPrinter.Length = 0 Then
                    Console.WriteLine("FATAL ERROR: Printer not defined in config!")
                    FatalError("Need a printer in the configuration file to continue")
                End If
                If Not RawPrinterHelper.ValidPrinter(myPrinter) Then
                    Console.WriteLine("FATAL ERROR: Printer defined in config not found!")
                    Console.WriteLine("Printer from config: " & myPrinter)
                    FatalError("Printer Not Found")
                End If
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CommonConfig")
            If Not configNode Is Nothing Then
                Dim commonConfig As String = configNode.InnerText.Trim
                If commonConfig.Length > 0 Then
                    Dim configFile2 As New XmlDocument
                    Try
                        configFile2.Load(commonConfig)
                        configNode = configFile2.SelectSingleNode("HZReceipt")
                        If Not configNode Is Nothing Then
                            configFile = configFile2
                        End If
                        If allowUpdate Then
                            configNode = configFile.SelectSingleNode("HZReceipt/CommonConfig")
                            If Not configNode Is Nothing Then
                                Dim configElement As XmlNode = configNode.Attributes.GetNamedItem("replace")
                                configNode = configFile.SelectSingleNode("HZReceipt")
                                If configElement Is Nothing Then
                                    insertReplaceValue(configFile, configNode, "CommonConfig", commonConfig)
                                ElseIf Not configElement.Value.Trim.Equals("1") Then
                                    insertReplaceValue(configFile, configNode, "CommonConfig", commonConfig)
                                End If
                            Else
                                configNode = configFile.SelectSingleNode("HZReceipt")
                                insertReplaceValue(configFile, configNode, "CommonConfig", commonConfig)
                            End If
                            insertReplaceValue(configFile, configNode, "Printer", myPrinter)
                            Try
                                configFile.Save(configFilename)
                            Catch ex As Exception
                                myErrorString &= "Could not update local config file from common config." & vbCrLf
                                Console.WriteLine("Could not update local config file from common config file.")
                                Console.WriteLine("Error: " & ex.Message)
                            End Try
                        End If
                    Catch ex As Exception
                        myErrorString &= "Could not load common config." & vbCrLf & "Using local config." & vbCrLf
                        Console.WriteLine("Could not load common config file.")
                        Console.WriteLine("Error: " & ex.Message)
                        Console.WriteLine("Resuming read of local config file.")
                    End Try
                End If
            End If
            'configNode = configFile.SelectSingleNode("HZReceipt/ConfigHorizon")
            'If Not configNode Is Nothing Then
            '    myConfigHorizon = configNode.InnerText.Trim.Equals("1")
            'End If
            configNode = configFile.SelectSingleNode("HZReceipt/TopMessageFile")
            If Not configNode Is Nothing Then
                myTopMessageFile = configNode.InnerText.Trim
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/BottomMessageFile")
            If Not configNode Is Nothing Then
                myBottomMessageFile = configNode.InnerText.Trim
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/PhoneNumber")
            If Not configNode Is Nothing Then
                myPhoneNumber = configNode.InnerText.Trim
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/WhiteSpaceTop")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myWhiteSpaceTop)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/WhiteSpaceBottom")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myWhiteSpaceBottom)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/AgencyFile")
            If Not configNode Is Nothing Then
                myAgencyFile = configNode.InnerText.Trim
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/AutoCut")
            If Not configNode Is Nothing Then
                myAutoCut = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/NamePosition")
            If Not configNode Is Nothing Then
                Select Case configNode.InnerText.Trim.ToLower
                    Case "topflipped", "flipped"
                        myHoldNamePosition = HoldNamePosition.TopFlipped
                    Case "bottom"
                        myHoldNamePosition = HoldNamePosition.Bottom
                    Case "off"
                        myHoldNamePosition = HoldNamePosition.Off
                    Case Else
                        myHoldNamePosition = HoldNamePosition.Top
                End Select
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/ShowDate")
            If Not configNode Is Nothing Then
                Select Case configNode.InnerText.Trim.ToLower
                    Case "left"
                        myHoldDate = HoldDate.Left
                    Case "right"
                        myHoldDate = HoldDate.Right
                    Case "center"
                        myHoldDate = HoldDate.Center
                    Case Else
                        myHoldDate = HoldDate.None
                End Select
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/LastNameSize")
            If Not configNode Is Nothing Then
                Select Case configNode.InnerText.Trim.ToLower
                    Case "normal"
                        myHoldLastNameSize = LastNameSize.Normal
                    Case "double"
                        myHoldLastNameSize = LastNameSize.Doubl
                    Case "triple"
                        myHoldLastNameSize = LastNameSize.Tripl
                End Select
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/FirstNameSize")
            If Not configNode Is Nothing Then
                Select Case configNode.InnerText.Trim.ToLower
                    Case "normal"
                        myHoldFirstNameSize = FirstNameSize.Normal
                    Case "double"
                        myHoldFirstNameSize = FirstNameSize.Doubl
                    Case "triple"
                        myHoldFirstNameSize = FirstNameSize.Tripl
                End Select
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/DateAdd")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldDateAdd)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/DateYear")
            If Not configNode Is Nothing Then
                myHoldDateYear = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/ShowOriginalDate")
            If Not configNode Is Nothing Then
                myHoldCircDate = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/ShowPatronBarcode")
            If Not configNode Is Nothing Then
                myHoldPatronBarcode = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/MaskPatronBarcode")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldPatronBarcodeMask)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/ShowEMail")
            If Not configNode Is Nothing Then
                myHoldEMail = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/ShowPhone")
            If Not configNode Is Nothing Then
                myHoldPhone = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/PhoneLarger")
            If Not configNode Is Nothing Then
                myHoldPhoneLarger = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/WhiteSpaceBottom")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldWhiteSpaceBottom)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/CallSlips")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldCallSlips)
                If myHoldCallSlips < 0 Or myHoldCallSlips > 2 Then
                    myHoldCallSlips = 0
                End If
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckOutSlips/AddPhone")
            If Not configNode Is Nothing Then
                myCheckoutPhone = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckOutSlips/MaskPatronBarcode")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myCheckoutPatronBarcodeMask)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckOutSlips/EnhanceDueDates")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myCheckOutDueDates)
                'myCheckoutDueDates = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckInSlips/AddPhone")
            If Not configNode Is Nothing Then
                myCheckinPhone = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckInSlips/MaskPatronBarcode")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myCheckinPatronBarcodeMask)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/CheckInSlips/Disable")
            If Not configNode Is Nothing Then
                myCheckinOff = configNode.InnerText.Trim.Equals("1")
                '   myCheckinTempForce.Enabled = myCheckinOff
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/TransitSlips/WhiteSpaceBottom")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myTransitWhiteSpaceBottom)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/TransitSlips/MiniWhiteSpaceBottom")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myTransitMiniWhiteSpaceBottom)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/PaymentSlips/Prompt")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myPaymentSlips)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldSlips/MaskUserBarcode")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myPaymentPatronBarcodeMask)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldWrappers/HoldWrappers") 'new
            If Not configNode Is Nothing Then
                myHoldWrapper = configNode.InnerText.Trim.Equals("1")
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldWrappers/UserIDLength")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldWrapperUserIDLength)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldWrappers/UserLastNameLength")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldWrapperUserLastNameLength)
            End If
            configNode = configFile.SelectSingleNode("HZReceipt/HoldWrappers/UserFirstNameLength")
            If Not configNode Is Nothing Then
                Integer.TryParse(configNode.InnerText, myHoldWrapperUserFirstNameLength)
            End If
            'end new
        End If
        Console.WriteLine("End Config Load")
    End Sub

    Private Sub LoadAgencies()
        Try
            Console.WriteLine("Start Agency File Load")
            myAgencyDocument.Load(myAgencyFile)
            Console.WriteLine("Agency File Loaded")
            myAgenciesDisabled = False
        Catch ex As Exception
            myErrorString &= "Could not load agencies file." & vbCrLf & "Transit Slip Agency Support Disabled." & vbCrLf
            Console.WriteLine("Agencies Load Error:")
            Console.WriteLine(ex.Message)
            Console.WriteLine("Transit Slip Agency Support Disabled")
            myAgenciesDisabled = True
            Exit Sub
        End Try
    End Sub

    Private Sub FatalError(ByVal ErrorString As String)
        If myErrorString.Length > 0 Then
            MsgBox("Fatal Error: " & ErrorString & vbCrLf & "Cannot continue." & vbCrLf & "Other Errors:" & vbCrLf & myErrorString.Trim & vbCrLf & vbCrLf & "Run via debug program for more details.", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Symphony Receipt Printer Error")
        Else
            MsgBox("Fatal Error: " & ErrorString & vbCrLf & "Cannot continue." & vbCrLf & vbCrLf & "Run via debug program for more details.", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Symphony Receipt Printer Error")
        End If
        End
    End Sub

    Private Sub ProcessReceipt(ByRef receipt As String)
        Static receiptType As New Regex("^\w*") '("^\s*(.+)")
        Dim newreceipt As String = epsonInitialize

        ' Nothing there?
        If receipt.Length = 0 Then
            Exit Sub
        End If

        Dim whiteSpace As Integer = 0
        If myWhiteSpaceTop > 5 Then
            myWhiteSpaceTop = 5
        End If
        If myWhiteSpaceTop > 0 Then
            newreceipt &= epsonLineFeedAmount10 & epsonLineFeed & Chr(20 * myWhiteSpaceTop) & vbCrLf
        End If

        newreceipt &= epsonLineFeedAmount10

        Dim regexMatch As Match = receiptType.Match(receipt)
        Cleanup(receipt) ' Do cleanup now that we already got our match block
        If regexMatch.Success Then

            Select Case regexMatch.Groups.Item(0).ToString.Trim.ToLower
                Case "hold"
                    ' Hold Slip
                    ProcessHold(receipt)
                    newreceipt &= receipt
                    whiteSpace = 0
                    If myHoldWhiteSpaceBottom > 5 Then
                        myHoldWhiteSpaceBottom = 5
                    End If
                    If myHoldWhiteSpaceBottom > 0 Then
                        newreceipt &= epsonLineFeedAmount10 & epsonLineFeed & Chr(20 * myHoldWhiteSpaceBottom) & vbCrLf
                    End If
                    If myAutoCut = True Then
                        newreceipt &= "-->End of Slip<--"
                    End If

                Case "transit"
                    ' Transit Slip
                    ProcessTransit(receipt)
                    If receipt.Length > 0 Then
                        newreceipt &= receipt
                        If myAutoCut = True Then
                            newreceipt &= "-->End of Slip<--"
                        End If
                    Else
                        Return
                    End If

                Case "checkout"
                    ProcessCheckout(receipt)
                    newreceipt &= receipt

                Case "renewal"
                    ProcessCheckout(receipt)
                    newreceipt &= receipt
                    If myAutoCut = True Then
                        newreceipt &= "-->End of Slip<--"
                    End If

                Case ""
                    'any subsequent titles on the checkout slips
                    ProcessCheckout(receipt)
                    'newreceipt = Regex.Replace(receipt, "-->Message1<--", TopMessage)
                    'newreceipt = Regex.Replace(newreceipt, "-->Message2<--", BottomMessage)
                    '    'add bottom message
                    '    newreceipt = Regex.Replace(receipt, "-->Bottom Message<--", BottomMessage)
                    newreceipt &= receipt
                    Dim endofslip As Regex = New Regex("-->End of Slip<--")
                    Dim eosmatch As Match = endofslip.Match(newreceipt)

                    If eosmatch.Success Then
                        If myWhiteSpaceBottom > 5 Then
                            myWhiteSpaceBottom = 5
                        End If
                        If myWhiteSpaceBottom > 0 Then
                            newreceipt &= epsonLineFeed & Chr(20 * myWhiteSpaceBottom) & vbCrLf
                        End If
                    End If
                    newreceipt = Regex.Replace(newreceipt, "-->End of Slip<--", "")

                Case "payment"
                    'If myCheckinOff And Not myCheckinTempForce.Checked Then
                    '    Return
                    'End If
                    ' If we are printing it, no major special handling beyond the message on top
                    ProcessCheckin(receipt)
                    If receipt.Length > 0 Then
                        newreceipt &= TopMessage() & receipt & BottomMessage()
                        If myAutoCut = True Then
                            newreceipt &= "-->End of Slip<--"
                        End If
                        '  myCheckinTempForce.Checked = False
                    Else
                        Return
                    End If
                Case Else
                    ' Nothing else to do
                    newreceipt &= receipt
            End Select
        Else
            ' This is odd, but whatever.
            newreceipt &= receipt
        End If

        'whiteSpace = 0
        'If myWhiteSpaceBottom > 5 Then
        '    myWhiteSpaceBottom = 5
        'End If
        'If myWhiteSpaceBottom > 0 Then
        '    newreceipt &= Chr(20 * myWhiteSpaceBottom) & vbCrLf
        'End If

        'newreceipt &= vbCrLf & " " & vbCrLf & " " & vbCrLf & epsonLineFeed & Chr(20) & vbCrLf
        newreceipt &= vbCrLf & " " & vbCrLf

        If myAutoCut Then
            Dim endofslip As Regex = New Regex("-->End of Slip<--")
            Dim eosmatch As Match = endofslip.Match(newreceipt)
            newreceipt = Regex.Replace(newreceipt, "-->End of Slip<--", "")
            If eosmatch.Success Then
                newreceipt &= vbCrLf & " " & vbCrLf & " " & vbCrLf & epsonLineFeed & Chr(20) & vbCrLf
                newreceipt &= epsonCutPaper
            End If
        End If

        RawPrinterHelper.SendStringToPrinter(myPrinter, newreceipt)
    End Sub

    Private Sub ProcessHold(ByRef receipt As String)
        ' For space/comma we want to grab everything up to the first comma as the last name, or everything after the last space
        ' This one regex accomplishes both nicely due to greedy/nongreedy capturing
        Static nameMatch As New Regex("User name:.([^," & vbCrLf & "]*)(,? )(.+?)$", RegexOptions.Multiline) 'User name:.*\n([^," & vbCrLf & "]*)(,? )(.+?)$"
        ' For other (single word), we need an empty second match group, at least, but treat the entire thing as "last name" later
        Static nameMatchSingle As New Regex("User name:.*\n(.*)(.{0})$", RegexOptions.Multiline)
        'Static phoneMatch As New Regex("Phone number: (.*)", RegexOptions.Multiline)
        'Static titleMatch As New Regex("^CALL NO.*\n(.*)$", RegexOptions.Multiline)
        ' Start with the date on top bit
        Dim dateString As String = ""
        'Dim callSlip As String = ""
        Dim holdwrapper As String = ""

        Dim HoldLastNameSize As String = ""
        Select Case myHoldLastNameSize
            Case LastNameSize.Doubl
                HoldLastNameSize = epsonSizeDouble
            Case LastNameSize.Tripl
                HoldLastNameSize = epsonSizeTriple
            Case LastNameSize.Normal
                HoldLastNameSize = epsonSizeNormal
        End Select

        Static HoldFirstNameSize As String = ""
        Select Case myHoldFirstNameSize
            Case FirstNameSize.Doubl
                HoldFirstNameSize = epsonSizeDouble
            Case FirstNameSize.Tripl
                HoldFirstNameSize = epsonSizeTriple
            Case FirstNameSize.Normal
                HoldFirstNameSize = epsonSizeNormal
        End Select

        If myHoldDate <> HoldDate.None Then
            If myHoldDateYear Then
                dateString = Now.AddDays(myHoldDateAdd).ToString("MM/dd/yyyy")
            Else
                dateString = Now.AddDays(myHoldDateAdd).ToString("MM/dd")
            End If
            dateString = epsonSizeDouble & dateString & epsonSizeNormal & vbCrLf
            ' Add right/center align
            Select Case myHoldDate
                Case HoldDate.Right
                    dateString = epsonAlignRight & dateString & epsonAlignLeft
                Case HoldDate.Center
                    dateString = epsonAlignCenter & dateString & epsonAlignLeft
            End Select
        End If
        ' Patron name
        Dim match As Match = nameMatch.Match(receipt)
        Dim lastName As String, firstName As String
        Dim nameString As String = ""
        If Not match.Success Then
            match = nameMatchSingle.Match(receipt)
        End If
        If match.Success Then
            ' We have a name!
            If match.Groups.Item(2).ToString.Equals(", ") Then
                ' Last, First
                lastName = match.Groups.Item(1).ToString.Trim
                firstName = match.Groups.Item(3).ToString.Trim
            ElseIf match.Groups.Item(2).ToString.Equals(" ") Then
                ' First Last
                firstName = match.Groups.Item(1).ToString.Trim
                lastName = match.Groups.Item(3).ToString.Trim
            Else
                firstName = ""
                lastName = match.Groups.Item(1).ToString
            End If
            If lastName.Length > 11 Then
                If myHoldNamePosition And HoldNamePosition.Flipped Then
                    If lastName.IndexOf("-") > 0 And lastName.IndexOf("-") <= 12 Then
                        '               callSlip = String.Join(vbCrLf & "-", lastName.Split("-".ToCharArray, 2))
                        lastName = String.Join(vbCrLf & "-", lastName.Split("-".ToCharArray, 2).Reverse)
                    Else
                        '          callSlip = String.Join(vbCrLf, lastName.Split(" ".ToCharArray, 2))
                        lastName = String.Join(vbCrLf, lastName.Split(" ".ToCharArray, 2).Reverse)
                    End If
                Else
                    If lastName.IndexOf("-") > 0 And lastName.IndexOf("-") <= 12 Then
                        lastName = String.Join(vbCrLf & "-", lastName.Split("-".ToCharArray, 2))
                    Else
                        lastName = String.Join(vbCrLf, lastName.Split(" ".ToCharArray, 2))
                    End If
                    '     callSlip = lastName
                End If
            Else
                ' callSlip = lastName
            End If

            If myHoldNamePosition And HoldNamePosition.Flipped Then
                nameString = HoldFirstNameSize & firstName & vbCrLf & HoldLastNameSize & lastName & epsonSizeNormal
                ' nameString = epsonSizeDouble & firstName & vbCrLf & epsonSizeTriple & lastName & epsonSizeNormal 'last name triple
            Else
                nameString = HoldLastNameSize & lastName & vbCrLf & HoldFirstNameSize & firstName & epsonSizeNormal
                'nameString = epsonSizeTriple & lastName & vbCrLf & epsonSizeDouble & firstName & epsonSizeNormal 'last name triple
            End If
            'End If
            'callSlip = epsonSizeTriple & callSlip & vbCrLf & epsonSizeDouble & firstName 'last name triple

            'Hold Wrappers
            If myHoldWrapper Then
                nameString = ""
                Dim newLastName As New Regex("(^\w{1," & myHoldWrapperUserLastNameLength & "})")
                Dim newLastNameMatch As Match = newLastName.Match(lastName)
                Dim LastNameTrim As String
                LastNameTrim = newLastNameMatch.Groups.Item(0).ToString.Trim

                Dim newFirstName As New Regex("(^\w{1," & myHoldWrapperUserFirstNameLength & "})")
                Dim newFirstNameMatch As Match = newFirstName.Match(firstName)
                Dim FirstNameTrim As String
                FirstNameTrim = newFirstNameMatch.Groups.Item(0).ToString.Trim

                'get User ID alone
                Dim userID As New Regex("(.*User ID) (.*)")
                Dim userIDMatch As Match = userID.Match(receipt)
                Dim UserIDAlone As String
                UserIDAlone = userIDMatch.Groups.Item(2).ToString.Trim
                Dim newUserID As New Regex("(\d{1," & myHoldWrapperUserIDLength & "})$")
                Dim newUserIDMatch As Match = newUserID.Match(UserIDAlone)
                Dim UserIDTrim As String
                UserIDTrim = newUserIDMatch.Groups.Item(1).ToString.Trim

                'get Pickup by date
                Dim pickupby As New Regex("(.*Pickup By:) (.*)")
                Dim pickupbyMatch As Match = pickupby.Match(receipt)
                Dim pickupbyDate As String
                pickupbyDate = pickupbyMatch.Groups.Item(0).ToString.Trim

                holdwrapper = LastNameTrim & vbCrLf & FirstNameTrim & vbCrLf & UserIDTrim & vbCrLf

                Dim pickup As String
                pickup = vbCrLf & pickupbyDate
                receipt = holdwrapper & pickup
            End If

        End If

        Select Case myHoldNamePosition
            Case HoldNamePosition.Top
                If nameString.Length > 0 Or dateString.Length > 0 Then
                    nameString &= vbCrLf & vbCrLf
                End If
                receipt = dateString & nameString & receipt
            Case HoldNamePosition.TopFlipped
                If nameString.Length > 0 Or dateString.Length > 0 Then
                    nameString &= vbCrLf & vbCrLf
                End If
                receipt = epsonInvertOn & dateString & nameString & epsonInvertOff & receipt
            Case HoldNamePosition.Bottom
                If nameString.Length > 0 Or dateString.Length > 0 Then
                    nameString = vbCrLf & vbCrLf & nameString
                End If
                receipt = receipt & nameString & epsonLineFeed & Chr(1) & dateString
        End Select



        ' Call Slip?
        'If myHoldCallSlips > 0 Then
        '    Dim phonenum As String = "", printCallSlip As Boolean = True
        '    ' match = phoneMatch.Match(receipt)
        '    If match.Success Then
        '        phonenum = match.Groups.Item(1).ToString.ToLower.Trim
        '        If phonenum.Length > 0 And (phonenum.IndexOf("email") > -1 Or phonenum.IndexOf("e-mail") > -1) Then
        '            If myHoldCallSlips = 1 Then
        '                printCallSlip = False
        '            Else
        '                phonenum = "*Notified by Email*"
        '            End If
        '        End If
        '    End If
        '    If printCallSlip Then
        '        callSlip &= vbCrLf & phonenum & vbCrLf & epsonSizeNormal
        '        'match = titleMatch.Match(receipt)
        '        If match.Success Then
        '            callSlip &= match.Groups.Item(1).ToString.Trim & vbCrLf
        '        End If
        '        callSlip &= " " & vbCrLf & epsonLineFeed & Chr(20) & vbCrLf & epsonCutPaper
        '    Else
        '        callSlip = ""
        '    End If
        'Else
        '    callSlip = ""
        'End If

        ' Remove information?
        If Not myHoldEMail Then
            receipt = Regex.Replace(receipt, "^E-mail:.*\n", "", RegexOptions.Multiline)
        End If
        '  If myHoldPatronBarcode Then
        '   receipt = Regex.Replace(receipt, "^.*Hold For:.*\n", "", RegexOptions.Multiline)
        If myHoldPatronBarcodeMask > 0 Then
            If myHoldPatronBarcodeMask > 14 Then
                myHoldPatronBarcodeMask = 14
            End If
            receipt = Regex.Replace(receipt, "^(.*User ID ).*(\d{" & myHoldPatronBarcodeMask.ToString & "}[^0-9]*)$", "$1" & New String("*", 14 - myHoldPatronBarcodeMask) & "$2", RegexOptions.Multiline)
        End If
        '    End If
        '  If Not myHoldPhone Then
        'receipt = Regex.Replace(receipt, "^Phone#:.*", "", RegexOptions.Multiline)
        If myHoldPhoneLarger Then
            receipt = Regex.Replace(receipt, "^Phone number: (.*)", epsonSizeDouble & "$1" & epsonSizeNormal, RegexOptions.Multiline)
        End If
        If Not myHoldCircDate Then
            receipt = Regex.Replace(receipt, "^" & epsonAlignCenter & "\d\d/\d\d/\d\d  \d\d:\d\d[AP]M.*\n", "", RegexOptions.Multiline)
        End If

        ' Main Circulation?
        ' receipt = callSlip & holdwrapper & Regex.Replace(receipt, "Main Circulation( [x123])?", "")
        If Not myHoldWrapper Then
            receipt = Regex.Replace(receipt, "Main Circulation( [x123])?,", "")
        End If
    End Sub

    Private Sub ProcessTransit(ByRef receipt As String)
        Static toRegex As New Regex("Transit to:(.*)", RegexOptions.Multiline)
        Static fromRegex As New Regex("Transit library:(.*)", RegexOptions.Multiline)
        Static agencySplit As New Regex("^(.{1,20})([ -/].*)$")
        Static transitpatron As New Regex("Name: (.*)", RegexOptions.Multiline)
        Dim regexMatch As Match
        Dim type As Integer = 0
        Dim toAgency As String = "Unknown"
        Dim fromAgency As String = "Unknown"
        Dim route As String = "______"
        Dim branch As String = String.Empty
        regexMatch = toRegex.Match(receipt)
        If regexMatch.Success Then
            toAgency = regexMatch.Groups(1).ToString().Trim()
        End If
        regexMatch = fromRegex.Match(receipt)
        If regexMatch.Success Then
            fromAgency = regexMatch.Groups(1).ToString().Trim()
        End If
        If Not myAgenciesDisabled Then
            Dim myAgency As XmlNode = myAgencyDocument.SelectSingleNode("Agencies/Agency[Name=""" & toAgency & """]")
            If Not myAgency Is Nothing Then
                Dim myNode As XmlNode
                myNode = myAgency.SelectSingleNode("SlipType")
                If Not myNode Is Nothing Then
                    Integer.TryParse(myNode.InnerText, type)
                End If
                If type = 2 Then ' No print slip?
                    receipt = ""
                    Return
                ElseIf type <> 1 Then ' Only full slips need Route/To/Branch config entries originally ElseIf type <> 1 then: jm
                    myNode = myAgency.SelectSingleNode("Route")
                    If Not myNode Is Nothing Then
                        route = myNode.InnerText
                    End If
                    myNode = myAgency.SelectSingleNode("To")
                    If Not myNode Is Nothing Then
                        toAgency = myNode.InnerText
                    End If
                    myNode = myAgency.SelectSingleNode("Branch")
                    If Not myNode Is Nothing Then
                        branch = myNode.InnerText
                    End If
                End If
            End If
        End If
        Dim newreceipt As String = ""
        If type = 1 Then ' Mini Slip
            If toAgency.Length > 20 Then
                Dim agencyMatch As Match = agencySplit.Match(toAgency)
                If agencyMatch.Success Then
                    If myAutoCut Then 'add line -->End of Slip<-- if autocut is enabled
                        receipt = epsonAlignCenter & epsonSizeDouble & agencyMatch.Groups(1).ToString _
                        & vbCrLf & agencyMatch.Groups(2).ToString & vbCrLf & epsonAlignLeft & epsonSizeNormal & "-->End of Slip<--"
                    Else 'Else don't print it, no need to
                        receipt = epsonAlignCenter & epsonSizeDouble & agencyMatch.Groups(1).ToString _
                        & vbCrLf & agencyMatch.Groups(2).ToString & vbCrLf & epsonAlignLeft & epsonSizeNormal
                    End If
                End If
            Else
                If myAutoCut Then 'add line -->End of Slip<-- if autocut is enabled
                    receipt = epsonAlignCenter & epsonSizeDouble & toAgency & vbCrLf & epsonAlignLeft & epsonSizeNormal & "-->End of Slip<--"
                Else 'Else don't print it, no need to
                    receipt = epsonAlignCenter & epsonSizeDouble & toAgency & vbCrLf & epsonAlignLeft & epsonSizeNormal
                End If
            End If
            Dim whiteSpace As Integer = 0
            If myTransitMiniWhiteSpaceBottom > 5 Then
                myTransitMiniWhiteSpaceBottom = 5
            End If
            If myTransitMiniWhiteSpaceBottom > 0 Then
                receipt &= epsonLineFeedAmount10 & epsonLineFeed & Chr(20 * myTransitMiniWhiteSpaceBottom) & vbCrLf
            End If

        Else ' Full Slip
            If type = 4 Then 'Full slip w/name big on top 
                Static nameMatch As New Regex("User name: (.*)", RegexOptions.Multiline)
                Dim match As Match = nameMatch.Match(receipt)
                Dim namestring As String = ""
                namestring = match.Groups.Item(1).ToString.Trim
                If match.Groups.Item(1).ToString.Contains(",") Then
                    receipt = Regex.Replace(receipt, "User name:.*", "") 'Dont do it if there's a comma
                Else
                    Dim namestringlarge = epsonSizeDouble & namestring & epsonSizeNormal & vbCrLf
                    receipt = Regex.Replace(receipt, "User name:.*", "")
                    newreceipt &= namestringlarge
                End If
            End If
            newreceipt &= epsonAlignCenter & epsonSizeSextuple & route & vbCrLf
            newreceipt &= epsonSizeDouble & toAgency & vbCrLf
            newreceipt &= epsonSizeNormal & branch & vbCrLf & epsonAlignLeft
            'newreceipt &= " " & vbCrLf & "Transit library:" & vbCrLf 'From:  'removed extra transit information from slips
            'newreceipt &= fromAgency & vbCrLf & " " & vbCrLf
            If type = 3 Then ' Full slip w/o name             
                receipt = Regex.Replace(receipt, "User name:.*", "")
            End If

            receipt = newreceipt & receipt

            Dim whiteSpace As Integer = 0
            If myTransitWhiteSpaceBottom > 5 Then
                myTransitWhiteSpaceBottom = 5
            End If
            If myTransitWhiteSpaceBottom > 0 Then
                receipt &= epsonLineFeedAmount10 & epsonLineFeed & Chr(20 * myTransitWhiteSpaceBottom) & vbCrLf
            End If
        End If

    End Sub

    Private Sub ProcessCheckout(ByRef receipt As String)
        ' First off, the top of the receipt should have the phone number attached
        ' If we have a phone number and it is turned on, anyway
        'If myCheckoutPhone And myPhoneNumber.Length > 0 Then
        '    receipt = Regex.Replace(receipt, "^" & epsonAlignCenter & "(Checkout Receipt|Date Due Slip)", "$0" & vbCrLf & epsonAlignCenter & myPhoneNumber)
        'End If
        receipt = Regex.Replace(receipt, "-->Message1<--", TopMessage)
        receipt = Regex.Replace(receipt, "-->Message2<--", BottomMessage)

        If myCheckoutPatronBarcodeMask > 0 Then
            If myCheckoutPatronBarcodeMask > 14 Then
                myCheckoutPatronBarcodeMask = 14
            End If
            receipt = Regex.Replace(receipt, "^(.*User ID: ).*(\d{" & myCheckoutPatronBarcodeMask.ToString & "}[^0-9]*)$", "$1" & New String("*", 14 - myCheckoutPatronBarcodeMask) & "$2", RegexOptions.Multiline)
        End If

        If myCheckoutDueDates = 1 Then
            ' Date Due with a date
            'receipt = Regex.Replace(receipt, "Title: (.*)", epsonEmphasisOn & "$1" & epsonEmphasisOff) 'something for future thought
            receipt = Regex.Replace(receipt, "Date due: (\d*/\d*/\d\d\d\d),(.*)", epsonSizeDouble & "Due: $1" & epsonSizeNormal & vbCrLf & "*******************************", RegexOptions.Multiline)
            ' Date due with NEVER due dates receipts
            receipt = Regex.Replace(receipt, "Date due: ([A-Z]*)", epsonSizeDouble & "Due: $1" & epsonSizeNormal & vbCrLf & "*******************************", RegexOptions.Multiline)
            ' receipt = epsonModePage & epsonPageSize & "AAAA" & epsonPrintPageMode & epsonModeStandard
        ElseIf myCheckoutDueDates = 2 Then
            ' Date Due with a date
            receipt = Regex.Replace(receipt, "Date due: (\d*/\d*/\d\d\d\d),(.*)", epsonEmphasisOn & "Due: $1" & epsonEmphasisOff & vbCrLf, RegexOptions.Multiline)
            ' Date due with NEVER due dates receipts
            receipt = Regex.Replace(receipt, "Date due: ([A-Z]*)", epsonEmphasisOn & "Due: $1" & epsonEmphasisOff & vbCrLf, RegexOptions.Multiline)
            End If

            ' Multi-item checkouts have underscore break points
            ' One at the top of each item, centered, 30 underscores
            ' One under each item, left aligned, 33 underscores
            ' Replace them with ***** breakpoints instead, and remove dupes
            ' First, the center aligned groups
            'receipt = Regex.Replace(receipt, "^" & epsonAlignCenter & "_{30}", epsonAlignCenter & "************************************", RegexOptions.Multiline)
            ' Second, the left aligned groups in front of center aligned groups
            'receipt = Regex.Replace(receipt, "_{33}" & vbCrLf & epsonAlignCenter, epsonAlignCenter, RegexOptions.Multiline)
            ' Third, the trailing left aligned group
            'receipt = Regex.Replace(receipt, "_{33}" & vbCrLf, epsonAlignCenter & "************************************" & vbCrLf & epsonAlignLeft, RegexOptions.Multiline)
    End Sub

    Private Sub ProcessCheckin(ByRef receipt As String) 'This needs to be changed to Payment
        If myPaymentSlips > 0 Then
            Static itemFind As New Regex("Bill reason:", RegexOptions.Multiline) 'originally item for checkin receipts
            Dim count As Integer = itemFind.Matches(receipt).Count
            If count > 0 Then 'originally 5
                If MsgBox("Do you want to print a Payment Receipt?", MsgBoxStyle.Exclamation + MsgBoxStyle.DefaultButton2 + MsgBoxStyle.YesNo + MsgBoxStyle.SystemModal, "Symphony Receipt Printer") = MsgBoxResult.No Then
                    ' If MsgBox("Check In Receipt has " & count.ToString & " items on it." & vbCrLf & vbCrLf & "Are you sure you want to print it?", MsgBoxStyle.Exclamation + MsgBoxStyle.DefaultButton2 + MsgBoxStyle.YesNo + MsgBoxStyle.SystemModal, "Symphony Receipt Printer") = MsgBoxResult.No Then
                    receipt = ""
                    Return
                End If
            End If
        End If
        ' First off, the top of the receipt should have the phone number attached
        ' If we have a phone number and it is turned on, anyway
        If myCheckinPhone And myPhoneNumber.Length > 0 Then
            receipt = Regex.Replace(receipt, "^" & epsonAlignCenter & "Checkin Receipt", "$0" & vbCrLf & epsonAlignCenter & myPhoneNumber)
        End If

        If myPaymentPatronBarcodeMask > 0 Then
            If myPaymentPatronBarcodeMask > 14 Then
                myPaymentPatronBarcodeMask = 14
            End If
            receipt = Regex.Replace(receipt, "^(.*User ID: ).*(\d{" & myPaymentPatronBarcodeMask.ToString & "}[^0-9]*)$", "$1" & New String("*", 14 - myCheckoutPatronBarcodeMask) & "$2", RegexOptions.Multiline)
        End If
    End Sub

    Private Function TopMessage() As String
        If myTopMessageFile.Length = 0 Then
            Return ""
        End If
        ' I could probably make this more static or something
        ' But I like the idea of being able to change it on the fly
        Dim message As String
        Try
            message = File.ReadAllText(myTopMessageFile)
            If message.Length > 0 Then
                'message = epsonAlignCenter & message & vbCrLf & " " & vbCrLf & epsonAlignLeft
                message = vbCrLf & " " & vbCrLf & epsonAlignCenter & "************************************" _
                & vbCrLf & message & vbCrLf & "************************************" & vbCrLf & epsonAlignLeft
            End If
            Return message
        Catch ex As Exception
            Console.WriteLine("Could not open/read top message file: " & myTopMessageFile)
            Console.WriteLine("Error:" & ex.Message)
            ' Do Nothing
            Return ""
        End Try
    End Function

    Private Function BottomMessage() As String
        If myBottomMessageFile.Length = 0 Then
            Return ""
        End If
        ' I could probably make this more static or something
        ' But I like the idea of being able to change it on the fly
        Dim message As String
        Try
            message = File.ReadAllText(myBottomMessageFile)
            If message.Length > 0 Then
                'message = epsonAlignCenter & message & vbCrLf & " " & vbCrLf & epsonAlignLeft
                message = vbCrLf & " " & vbCrLf & epsonAlignCenter & "************************************" _
                & vbCrLf & message & vbCrLf & "************************************" & vbCrLf & epsonAlignLeft
            End If
            Return message
        Catch ex As Exception
            Console.WriteLine("Could not open/read bottom message file: " & myBottomMessageFile)
            Console.WriteLine("Error:" & ex.Message)
            ' Do Nothing
            Return ""
        End Try
    End Function

    Private Sub Cleanup(ByRef receipt As String)
        ' Seems that we get 99 char wide files
        ' Correct the spacing with a couple of replaces to go with alignment codes instead
        ' First, we want to strip off the ^L, usually at the end of the file
        receipt = receipt.Replace(Chr(12), "")
        ' Fix line endings
        receipt = Regex.Replace(receipt, vbCr & "([^" & vbLf & "])", vbCrLf & "$1")
        ' Then, we check for anything to be right-aligned.
        ' We determine this based on having spaces for the first 50 or so chars
        receipt = Regex.Replace(receipt, "^(?:" & epsonAlignLeft & ")? {50,}([^ ].*)\n", epsonAlignRight & "$1" & vbCrLf & epsonAlignLeft, RegexOptions.Multiline)
        ' Centered stuff should be left with a pile of spaced on the front, we use 10
        receipt = Regex.Replace(receipt, "^(?:" & epsonAlignLeft & ")? {10,}([^ ].*)\n", epsonAlignCenter & "$1" & vbCrLf & epsonAlignLeft, RegexOptions.Multiline)
        ' Clean up doubled alignment commands, stripping off the left if there is a right/center after it
        receipt = Regex.Replace(receipt, "^" & epsonAlignLeft & "(" & epsonAlignRight & "|" & epsonAlignCenter & ")", "$1", RegexOptions.Multiline)
        ' Clean up leftover whitespace
        receipt = Regex.Replace(receipt, "^(" & epsonAlignLeft & "|" & epsonAlignCenter & "|" & epsonAlignRight & ")? *(.*?) *$", "$1$2", RegexOptions.Multiline)
        ' Clean up ### entries before call number line, and trim file
        receipt = Regex.Replace(receipt, "^(" & epsonAlignLeft & ")? *\d{3}" & vbCrLf & "CALL NO", "CALL NO", RegexOptions.Multiline).Trim()
        ' Blank lines get an added space so they are "full height"
        receipt = Regex.Replace(receipt, "^(" & epsonAlignLeft & ")?$", "$0 ", RegexOptions.Multiline)
    End Sub

    Private Sub insertReplaceValue(ByRef xmlDoc As XmlDocument, ByRef parentNode As XmlNode, ByRef nodeName As String, Optional ByRef value As String = Nothing)
        Dim targetNode As XmlNode = parentNode.SelectSingleNode(nodeName)
        If targetNode Is Nothing Then
            targetNode = xmlDoc.CreateElement(nodeName)
            If Not value Is Nothing Then
                targetNode.InnerText = value
            End If
            parentNode.AppendChild(targetNode)
        ElseIf Not value Is Nothing Then
            targetNode.InnerText = value
        End If
    End Sub

    Private Sub pipeCallBack(ByVal result As IAsyncResult)
        Dim i As Integer
        Dim receipt As String = String.Empty
        Dim reader As StreamReader
        For i = 0 To pipeServer.GetUpperBound(0)
            If result Is myPipeResult(i) Then
                Exit For
            End If
        Next
        If Not pipeOpen(i) Then
            Exit Sub
        End If
        pipeServer(i).EndWaitForConnection(result)
        reader = New StreamReader(pipeServer(i))
        receipt = reader.ReadToEnd()
        pipeServer(i).Disconnect()
        myPipeResult(i) = pipeServer(i).BeginWaitForConnection(pipeAsyncCallBack, pipeOpen(i))
        If receipt.Trim.Length > 0 Then
            ProcessReceipt(receipt)
        End If
    End Sub

    'Sub configHorizon(Optional ByVal sender As Object = Nothing, Optional ByVal e As EventArgs = Nothing)
    '    Dim regKey As RegistryKey
    '    ' First, we take the receipt printer settings and cram them in. Most of them are binary values, hence the byte arrays
    '    Registry.CurrentUser.CreateSubKey("SOFTWARE\Dynix\Horizon74\MUC\Receipt Printer\Horizon Receipt Printer")
    '    regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\Dynix\Horizon74\MUC\Receipt Printer\Horizon Receipt Printer", True)
    '    regKey.SetValue("LogFont", New Byte() {&H64, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H90, &H1, &H0, &H0, &H0, _
    '        &H0, &H0, &H0, &H1, &H2, &H1, &H31, &H44, &H0, &H65, &H0, &H76, &H0, &H69, &H0, &H63, &H0, &H65, &H0, &H20, &H0, &H46, &H0, &H6F, &H0, _
    '        &H6E, &H0, &H74, &H0, &H20, &H0, &H31, &H0, &H32, &H0, &H63, &H0, &H70, &H0, &H69, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, _
    '        &H0, &H64, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0})
    '    regKey.SetValue("FontSize", "10")
    '    regKey.SetValue("dnSize", New Byte() {&H70, &H0, &H0, &H0})
    '    regKey.SetValue("devnames", New Byte() {&H4, &H0, &HD, &H0, &H25, &H0, &H0, &H0, &H77, &H0, &H69, &H0, &H6E, &H0, &H73, &H0, &H70, &H0, &H6F, &H0, &H6F, _
    '        &H0, &H6C, &H0, &H0, &H0, &H48, &H0, &H6F, &H0, &H72, &H0, &H69, &H0, &H7A, &H0, &H6F, &H0, &H6E, &H0, &H20, &H0, &H52, &H0, &H65, &H0, _
    '        &H63, &H0, &H65, &H0, &H69, &H0, &H70, &H0, &H74, &H0, &H20, &H0, &H50, &H0, &H72, &H0, &H69, &H0, &H6E, &H0, &H74, &H0, &H65, &H0, &H72, _
    '        &H0, &H0, &H0, &H5C, &H0, &H5C, &H0, &H2E, &H0, &H5C, &H0, &H70, &H0, &H69, &H0, &H70, &H0, &H65, &H0, &H5C, &H0, &H48, &H0, &H5A, &H0, _
    '        &H52, &H0, &H65, &H0, &H63, &H0, &H65, &H0, &H69, &H0, &H70, &H0, &H74, &H0, &H0, &H0})
    '    regKey.SetValue("dmOrient", New Byte() {&H1, &H0})
    '    regKey.SetValue("dmPSize", New Byte() {&H1, &H0})
    '    regKey.SetValue("dmPLength", New Byte() {&HEA, &HA})
    '    regKey.SetValue("dmPWidth", New Byte() {&H6F, &H8})
    '    regKey.SetValue("dmScale", New Byte() {&H64, &H0})
    '    regKey.SetValue("dmCopies", New Byte() {&H1, &H0})
    '    regKey.SetValue("dmPQuality", New Byte() {&H58, &H2})
    '    regKey.SetValue("dmColor", New Byte() {&H1, &H0})
    '    regKey.SetValue("dmDuplex", New Byte() {&H1, &H0})
    '    regKey.SetValue("dmYRes", New Byte() {&H58, &H2})
    '    regKey.SetValue("dmTTOption", New Byte() {&H2, &H0})
    '    regKey.SetValue("dmCollate", New Byte() {&H0, &H0})
    '    regKey.SetValue("dmFormName", "Letter")

    '    ' Next, Circ Settings
    '    Registry.CurrentUser.CreateSubKey("SOFTWARE\Dynix\Horizon74\CRC")
    '    regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\Dynix\Horizon74\CRC", True)
    '    regKey.SetValue("ALLOW_SLIP_PRINTING", "1")
    '    regKey.SetValue("SLIP_PRINTER_NAME", "Horizon Receipt Printer")
    '    regKey.SetValue("SLIP_WINDOWS_PRINTING", "1")

    '    ' Next....DONE! Say something if this was a menu call
    '    If Not sender Is Nothing Then
    '        myNotifyIcon.ShowBalloonTip(30, "Symphony Receipt Printer", "Symphony Configuration Installed" & vbCrLf & "Restart Symphony to see changes", ToolTipIcon.Info)
    '    End If
    'End Sub
End Module
